<!DOCTYPE html>



  


<html class="theme-next gemini use-motion" lang="zh-Hans">
<head><meta name="generator" content="Hexo 3.8.0">
  <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform">
<meta http-equiv="Cache-Control" content="no-siteapp">
















  
  
  <link href="/limh/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css">







<link href="/limh/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css">

<link href="/limh/css/main.css?v=5.1.4" rel="stylesheet" type="text/css">


  <link rel="apple-touch-icon" sizes="180x180" href="/limh/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/limh/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/limh/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/limh/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="教程,">










<meta name="description" content="家电遥控器通信距离往往要求不高，而红外的成本比其它无线设备要低的多，所以家电遥控器应用中红外始终占据着一席之地。遥控器的基带通信协议很多，大概有几十种，常用的就有 ITT 协议、NEC 协议、Sharp 协议、Philips RC-5 协议、Sony SIRC 协议等。用的最多的就是 NEC 协议了，因此我们 KST-51 开发板配套的遥控器直接采用 NEC 协议，我们这节课也以 NEC 协议标准">
<meta name="keywords" content="教程">
<meta property="og:type" content="article">
<meta property="og:title" content="16.3、NEC协议红外遥控器">
<meta property="og:url" content="https://tdnr.gitee.io/limh/2014/12/01/mcu108/index.html">
<meta property="og:site_name" content="土逗牛肉">
<meta property="og:description" content="家电遥控器通信距离往往要求不高，而红外的成本比其它无线设备要低的多，所以家电遥控器应用中红外始终占据着一席之地。遥控器的基带通信协议很多，大概有几十种，常用的就有 ITT 协议、NEC 协议、Sharp 协议、Philips RC-5 协议、Sony SIRC 协议等。用的最多的就是 NEC 协议了，因此我们 KST-51 开发板配套的遥控器直接采用 NEC 协议，我们这节课也以 NEC 协议标准">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="http://c.biancheng.net/cpp/uploads/allimg/140322/1-14032216235A59.png">
<meta property="og:image" content="http://c.biancheng.net/cpp/uploads/allimg/140322/1-140322162530535.png">
<meta property="og:image" content="http://c.biancheng.net/cpp/uploads/allimg/140322/1-140322162I5911.png">
<meta property="og:image" content="http://c.biancheng.net/cpp/uploads/allimg/140323/1-140323222920F7.png">
<meta property="og:image" content="http://c.biancheng.net/cpp/uploads/allimg/140323/1-14032322310XM.png">
<meta property="og:updated_time" content="2019-01-31T13:14:53.366Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="16.3、NEC协议红外遥控器">
<meta name="twitter:description" content="家电遥控器通信距离往往要求不高，而红外的成本比其它无线设备要低的多，所以家电遥控器应用中红外始终占据着一席之地。遥控器的基带通信协议很多，大概有几十种，常用的就有 ITT 协议、NEC 协议、Sharp 协议、Philips RC-5 协议、Sony SIRC 协议等。用的最多的就是 NEC 协议了，因此我们 KST-51 开发板配套的遥控器直接采用 NEC 协议，我们这节课也以 NEC 协议标准">
<meta name="twitter:image" content="http://c.biancheng.net/cpp/uploads/allimg/140322/1-14032216235A59.png">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/limh/',
    scheme: 'Gemini',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://tdnr.gitee.io/limh/2014/12/01/mcu108/">





  <title>16.3、NEC协议红外遥控器 | 土逗牛肉</title>
  








</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans ">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/limh/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">土逗牛肉</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle"></p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/limh/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br>
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/limh/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br>
            
            归档
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/limh/tags/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br>
            
            标签
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/limh/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br>
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-about">
          <a href="/limh/about/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br>
            
            关于
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://tdnr.gitee.io/limh/limh/2014/12/01/mcu108/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="土逗牛肉">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/limh/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="土逗牛肉">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">16.3、NEC协议红外遥控器</h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2014-12-01T20:41:08+08:00">
                2014-12-01
              </time>
            

            

            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/limh/categories/单片机教程/" itemprop="url" rel="index">
                    <span itemprop="name">单片机教程</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <p>家电遥控器通信距离往往要求不高，而红外的成本比其它无线设备要低的多，所以家电遥控器应用中红外始终占据着一席之地。遥控器的基带通信协议很多，大概有几十种，常用的就有 ITT 协议、NEC 协议、Sharp 协议、Philips RC-5 协议、Sony SIRC 协议等。用的最多的就是 NEC 协议了，因此我们 KST-51 开发板配套的遥控器直接采用 NEC 协议，我们这节课也以 NEC 协议标准来讲解一下。</p>
<p>NEC 协议的数据格式包括了引导码、用户码、用户码（或者用户码反码）、按键键码和键码反码，最后一个停止位。停止位主要起隔离作用，一般不进行判断，编程时我们也不予理会。其中数据编码总共是 4 个字节 32 位，如图 16-7 所示。第一个字节是用户码，第二个字节可能也是用户码，或者是用户码的反码，具体由生产商决定，第三个字节就是当前按键的键数据码，而第四个字节是键数据码的反码，可用于对数据的纠错。</p>
<p><img src="http://c.biancheng.net/cpp/uploads/allimg/140322/1-14032216235A59.png" alt></p>
<center><strong>图 16-7  NEC 协议数据格式</strong></center>

<p>这个 NEC 协议，表示数据的方式不像我们之前学过的比如 UART 那样直观，而是每一位数据本身也需要进行编码，编码后再进行载波调制。</p>
<ul>
<li>引导码：9ms 的载波+4.5ms 的空闲。</li>
<li>比特值“0”：560us 的载波+560us 的空闲。</li>
<li>比特值“1”：560us 的载波+1.68ms 的空闲。</li>
</ul>
<p>结合图 16-7 我们就能看明白了，最前面黑乎乎的一段，是引导码的 9ms 载波，紧接着是引导码的 4.5ms 的空闲，而后边的数据码，是众多载波和空闲交叉，它们的长短就由其要传递的具体数据来决定。HS0038B 这个红外一体化接收头，当收到有载波的信号的时候，会输出一个低电平，空闲的时候会输出高电平，我们用逻辑分析仪抓出来一个红外按键通过HS0038B 解码后的图形来了解一下，如图 16-8 所示。</p>
<p><img src="http://c.biancheng.net/cpp/uploads/allimg/140322/1-140322162530535.png" alt></p>
<center><strong>图 16-8  红外遥控器按键编码</strong></center>

<p>从图上可以看出，先是 9ms 载波加 4.5ms 空闲的起始码，数据码是低位在前，高位在后，数据码第一个字节是 8 组 560us 的载波加 560us 的空闲，也就是 0x00，第二个字节是 8 组 560us的载波加 1.68ms 的空闲，可以看出来是 0xFF，这两个字节就是用户码和用户码的反码。按键的键码二进制是 0x0C，反码就是 0xF3，最后跟了一个 560us 载波停止位。对于我们的遥控器来说，不同的按键，就是键码和键码反码的区分，用户码是一样的。这样我们就可以通过单片机的程序，把当前的按键的键码给解析出来。</p>
<p>我们前边学习中断的时候，学到 51 单片机有外部中断 0 和外部中断 1 这两个外部中断。我们的红外接收引脚接到了 P3.3 引脚上，这个引脚的第二功能就是外部中断 1。在寄存器TCON 中的 bit3 和 bit2 这两位，是和外部中断 1 相关的两位。其中 IE1 是外部中断标志位，当外部中断发生后，这一位被自动置 1，和定时器中断标志位 TF 相似，进入中断后会自动清零，也可以软件清零。bit2 是设置外部中断类型的，如果 bit2 为 0，那么只要 P3.3 为低电平就可以触发中断，如果 bit2 为 1，那么 P3.3 从高电平到低电平的下降沿发生才可以触发中断。此外，外部中断 1 使能位是 EX1。那下面我们就把程序写出来，使用数码管把遥控器的用户码和键码显示出来。</p>
<p>Infrared.c 文件主要是用来检测红外通信的，当发生外部中断后，进入外部中断，通过定时器 1 定时，首先对引导码判断，而后对数据码的每个位逐位获取高低电平的时间，从而得知每一位是 0 还是 1，最终把数据码解出来。虽然最终实现的功能很简单，但因为编码本身的复杂性，使得红外接收的中断程序在逻辑上显得就比较复杂，那么我们首先提供出中断函数的程序流程图，大家可以对照流程图来理解程序代码，如图 16-9 所示。</p>
<p><img src="http://c.biancheng.net/cpp/uploads/allimg/140322/1-140322162I5911.png" alt></p>
<center><strong>图 16-9  红外接收程序流程图</strong></center>

<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br></pre></td><td class="code"><pre><span class="line">/***************************Infrared.c 文件程序源代码*****************************/</span><br><span class="line">#include &lt;reg52.h&gt;</span><br><span class="line">sbit IR_INPUT = P3^3; //红外接收引脚</span><br><span class="line">bit irflag = 0; //红外接收标志，收到一帧正确数据后置 1</span><br><span class="line">unsigned char ircode[4]; //红外代码接收缓冲区</span><br><span class="line">/* 初始化红外接收功能 */</span><br><span class="line">void InitInfrared()&#123;</span><br><span class="line">    IR_INPUT = 1; //确保红外接收引脚被释放</span><br><span class="line">    TMOD &amp;= 0x0F; //清零 T1 的控制位</span><br><span class="line">    TMOD |= 0x10; //配置 T1 为模式 1</span><br><span class="line">    TR1 = 0; //停止 T1 计数</span><br><span class="line">    ET1 = 0; //禁止 T1 中断</span><br><span class="line">    IT1 = 1; //设置 INT1 为负边沿触发</span><br><span class="line">    EX1 = 1; //使能 INT1 中断</span><br><span class="line">&#125;</span><br><span class="line">/* 获取当前高电平的持续时间 */</span><br><span class="line">unsigned int GetHighTime()&#123;</span><br><span class="line">    TH1 = 0; //清零 T1 计数初值</span><br><span class="line">    TL1 = 0;</span><br><span class="line">    TR1 = 1; //启动 T1 计数</span><br><span class="line">    while (IR_INPUT)&#123; //红外输入引脚为 1 时循环检测等待，变为 0 时则结束本循环</span><br><span class="line">        //当 T1 计数值大于 0x4000，即高电平持续时间超过约 18ms 时，</span><br><span class="line">        //强制退出循环，是为了避免信号异常时，程序假死在这里。</span><br><span class="line">        if (TH1 &gt;= 0x40)&#123;</span><br><span class="line">            break;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    TR1 = 0; //停止 T1 计数</span><br><span class="line">    return (TH1*256 + TL1); //T1 计数值合成为 16bit 整型数，并返回该数</span><br><span class="line">&#125;</span><br><span class="line">/* 获取当前低电平的持续时间 */</span><br><span class="line">unsigned int GetLowTime()&#123;</span><br><span class="line">    TH1 = 0; //清零 T1 计数初值</span><br><span class="line">    TL1 = 0;</span><br><span class="line">    TR1 = 1; //启动 T1 计数</span><br><span class="line">    while (!IR_INPUT)&#123; //红外输入引脚为 0 时循环检测等待，变为 1 时则结束本循环</span><br><span class="line">        //当 T1 计数值大于 0x4000，即低电平持续时间超过约 18ms 时，</span><br><span class="line">        //强制退出循环，是为了避免信号异常时，程序假死在这里。</span><br><span class="line">        if (TH1 &gt;= 0x40)&#123;</span><br><span class="line">            break;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    TR1 = 0; //停止 T1 计数</span><br><span class="line">    return (TH1*256 + TL1); //T1 计数值合成为 16bit 整型数，并返回该数</span><br><span class="line">&#125;</span><br><span class="line">/* INT1 中断服务函数，执行红外接收及解码 */</span><br><span class="line">void EXINT1_ISR() interrupt 2&#123;</span><br><span class="line">    unsigned char i, j;</span><br><span class="line">    unsigned char byt;</span><br><span class="line">    unsigned int time;</span><br><span class="line">   </span><br><span class="line">    //接收并判定引导码的 9ms 低电平</span><br><span class="line">    time = GetLowTime();</span><br><span class="line">    //时间判定范围为 8.5～9.5ms，</span><br><span class="line">    //超过此范围则说明为误码，直接退出</span><br><span class="line">    if ((time&lt;7833) || (time&gt;8755))&#123;</span><br><span class="line">        IE1 = 0; //退出前清零 INT1 中断标志</span><br><span class="line">        return;</span><br><span class="line">    &#125;</span><br><span class="line">    //接收并判定引导码的 4.5ms 高电平</span><br><span class="line">    time = GetHighTime();</span><br><span class="line">    //时间判定范围为 4.0～5.0ms，</span><br><span class="line">    //超过此范围则说明为误码，直接退出</span><br><span class="line">    if ((time&lt;3686) || (time&gt;4608))&#123;</span><br><span class="line">        IE1 = 0;</span><br><span class="line">        return;</span><br><span class="line">    &#125;</span><br><span class="line">    //接收并判定后续的 4 字节数据</span><br><span class="line">    for (i=0; i&lt;4; i++)&#123; //循环接收 4 个字节</span><br><span class="line">        for (j=0; j&lt;8; j++)&#123; //循环接收判定每字节的 8 个 bit</span><br><span class="line">            //接收判定每 bit 的 560us 低电平</span><br><span class="line">            time = GetLowTime();</span><br><span class="line">            //时间判定范围为 340～780us，</span><br><span class="line">            //超过此范围则说明为误码，直接退出</span><br><span class="line">            if ((time&lt;313) || (time&gt;718))&#123;</span><br><span class="line">                IE1 = 0;</span><br><span class="line">                return;</span><br><span class="line">            &#125;</span><br><span class="line">            //接收每 bit 高电平时间，判定该 bit 的值</span><br><span class="line">            time = GetHighTime();</span><br><span class="line">            //时间判定范围为 340～780us，</span><br><span class="line">            //在此范围内说明该 bit 值为 0</span><br><span class="line">            if ((time&gt;313) &amp;&amp; (time&lt;718))&#123;</span><br><span class="line">                byt &gt;&gt;= 1; //因低位在先，所以数据右移，高位为 0</span><br><span class="line">                //时间判定范围为 1460～1900us，</span><br><span class="line">                //在此范围内说明该 bit 值为 1</span><br><span class="line">            &#125;else if ((time&gt;1345) &amp;&amp; (time&lt;1751))&#123;</span><br><span class="line">                byt &gt;&gt;= 1; //因低位在先，所以数据右移，</span><br><span class="line">                byt |= 0x80; //高位置 1</span><br><span class="line">            &#125;else&#123; //不在上述范围内则说明为误码，直接退出</span><br><span class="line">                IE1 = 0;</span><br><span class="line">                return;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        ircode[i] = byt; //接收完一个字节后保存到缓冲区</span><br><span class="line">    &#125;</span><br><span class="line">    irflag = 1; //接收完毕后设置标志</span><br><span class="line">    IE1 = 0; //退出前清零 INT1 中断标志</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>大家在阅读这个程序时，会发现我们在获取高低电平时间的时候做了超时判断 if(TH1 &gt;= 0x40)，这个超时判断主要是为了应对输入信号异常（比如意外的干扰等）情况的，如果不做超时判断，当输入信号异常时，程序就有可能会一直等待一个无法到来的跳变沿，而造成程序假死。</p>
<p>另外补充一点，遥控器的单按按键和持续按住按键发出来的信号是不同的。我们先来对比一下两种按键方式的实测信号波形，如图 16-10 和 16-11 所示。</p>
<p><img src="http://c.biancheng.net/cpp/uploads/allimg/140323/1-140323222920F7.png" alt></p>
<center><strong>图16-10   红外单次按键时序图</strong></center>

<p><img src="http://c.biancheng.net/cpp/uploads/allimg/140323/1-14032322310XM.png" alt></p>
<center><strong>图 16-11  红外持续按键时序图</strong></center>

<p>单次按键的结果 16-9 和我们之前的图 16-8 是一样的，这个不需要再解释。而持续按键，首先会发出一个和单次按键一样的波形出来，经过大概 40ms 后，会产生一个 9ms 载波加2.25ms 空闲，再跟一个停止位的波形，这个叫做重复码，而后只要你还在按住按键，那么每隔约 108ms 就会产生一个重复码。对于这个重复码我们的程序并没有对它单独解析，而是直接忽略掉了，这并不影响对正常按键数据的接收。如果你日后做程序时需要用到这个重复码，那么只需要再把对重复码的解析添加进来就可以了。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line">/*****************************main.c 文件程序源代码******************************/</span><br><span class="line">#include &lt;reg52.h&gt;</span><br><span class="line">sbit ADDR3 = P1^3;</span><br><span class="line">sbit ENLED = P1^4;</span><br><span class="line">unsigned char code LedChar[] = &#123; //数码管显示字符转换表</span><br><span class="line">    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,</span><br><span class="line">    0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E</span><br><span class="line">&#125;;</span><br><span class="line">unsigned char LedBuff[6] = &#123; //数码管显示缓冲区</span><br><span class="line">    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF</span><br><span class="line">&#125;;</span><br><span class="line">unsigned char T0RH = 0; //T0 重载值的高字节</span><br><span class="line">unsigned char T0RL = 0; //T0 重载值的低字节</span><br><span class="line">extern bit irflag;</span><br><span class="line">extern unsigned char ircode[4];</span><br><span class="line">extern void InitInfrared();</span><br><span class="line">void ConfigTimer0(unsigned int ms);</span><br><span class="line">void main()&#123;</span><br><span class="line">    EA = 1; //开总中断</span><br><span class="line">    ENLED = 0; //使能选择数码管</span><br><span class="line">    ADDR3 = 1;</span><br><span class="line">    InitInfrared(); //初始化红外功能</span><br><span class="line">    ConfigTimer0(1); //配置 T0 定时 1ms</span><br><span class="line">    //PT0 = 1; //配置 T0 中断为高优先级，启用本行可消除接收时的闪烁</span><br><span class="line">   </span><br><span class="line">    while (1)&#123;</span><br><span class="line">        if (irflag)&#123; //接收到红外数据时刷新显示</span><br><span class="line">            irflag = 0;</span><br><span class="line">            LedBuff[5] = LedChar[ircode[0] &gt;&gt; 4]; //用户码显示</span><br><span class="line">            LedBuff[4] = LedChar[ircode[0]&amp;0x0F];</span><br><span class="line">            LedBuff[1] = LedChar[ircode[2] &gt;&gt; 4]; //键码显示</span><br><span class="line">            LedBuff[0] = LedChar[ircode[2]&amp;0x0F];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">/* 配置并启动 T0，ms-T0 定时时间 */</span><br><span class="line">void ConfigTimer0(unsigned int ms)&#123;</span><br><span class="line">    unsigned long tmp; //临时变量</span><br><span class="line">    tmp = 11059200 / 12; //定时器计数频率</span><br><span class="line">    tmp = (tmp * ms) / 1000; //计算所需的计数值</span><br><span class="line">    tmp = 65536 - tmp; //计算定时器重载值</span><br><span class="line">    tmp = tmp + 13; //补偿中断响应延时造成的误差</span><br><span class="line">    T0RH = (unsigned char)(tmp&gt;&gt;8); //定时器重载值拆分为高低字节</span><br><span class="line">    T0RL = (unsigned char)tmp;</span><br><span class="line">   </span><br><span class="line">    TMOD &amp;= 0xF0; //清零 T0 的控制位</span><br><span class="line">    TMOD |= 0x01; //配置 T0 为模式 1</span><br><span class="line">    TH0 = T0RH; //加载 T0 重载值</span><br><span class="line">    TL0 = T0RL;</span><br><span class="line">    ET0 = 1; //使能 T0 中断</span><br><span class="line">    TR0 = 1; //启动 T0</span><br><span class="line">&#125;</span><br><span class="line">/* LED 动态扫描刷新函数，需在定时中断中调用 */</span><br><span class="line">void LedScan()&#123;</span><br><span class="line">    static unsigned char i = 0; //动态扫描索引</span><br><span class="line">    P0 = 0xFF; //关闭所有段选位，显示消隐</span><br><span class="line">    P1 = (P1 &amp; 0xF8) | i; //位选索引值赋值到 P1 口低 3 位</span><br><span class="line">    P0 = LedBuff[i]; //缓冲区中索引位置的数据送到 P0 口</span><br><span class="line">    if (i &lt; sizeof(LedBuff)-1)&#123; //索引递增循环，遍历整个缓冲区</span><br><span class="line">        i++;</span><br><span class="line">    &#125;else&#123;</span><br><span class="line">        i = 0;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">/* T0 中断服务函数，执行数码管扫描显示 */</span><br><span class="line">void InterruptTimer0() interrupt 1&#123;</span><br><span class="line">    TH0 = T0RH; //重新加载重载值</span><br><span class="line">    TL0 = T0RL;</span><br><span class="line">    LedScan(); //数码管扫描显示</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>main.c 文件的主要功能就是把获取到的红外遥控器的用户码和键码信息，传送到数码管上显示出来，并且通过定时器 T0 的 1ms 中断进行数码管的动态刷新。不知道大家经过试验发现没有，当我们按下遥控器按键的时候，数码管显示的数字会闪烁，这是什么原因呢？单片机的程序都是顺序执行的，一旦我们按下遥控器按键，单片机就会进入遥控器解码的中断程序内，而这个程序执行的时间又比较长，要几十个毫秒，而如果数码管动态刷新间隔超过10ms 后就会感觉到闪烁，因此这个闪烁是由于程序执行红外解码时，延误了数码管动态刷新造成的。</p>
<p>如何解决？前边我们讲过中断优先级问题，如果设置了中断的抢占优先级，就会产生中断嵌套。中断嵌套的原理，我们在前边讲中断的时候已经讲过一次了，大家可以回头再复习一下。那么这个程序中，有 2 个中断程序，一个是外部中断程序负责接收红外数据，一个是定时器中断程序负责数码管扫描，要使红外接收不耽误数码管扫描的执行，那么就必须让定时器中断对外部中断实现嵌套，即把定时器中断设置为高抢占优先级。定时器中断程序，执行时间只有几十个 us，即使打断了红外接收中断的执行，也最多是给每个位的时间测量附加了几十 us 的误差，而这个误差在最短 560us 的时间判断中完全是容许的，所以中断嵌套并不会影响红外数据的正常接收。在 main 函数中，大家把这行程序“//PT0 = 1;”的注释取消，也就是使这行代码生效，这样就设置了 T0 中断的高抢占优先级，再编译一下，下载到单片机里，然后按键试试，是不是没有任何闪烁了呢？而中断嵌套的意义也有所体会了吧。</p>

      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/limh/tags/教程/" rel="tag"># 教程</a>
          
        </div>
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/limh/2014/12/01/mcu107/" rel="next" title="16.2、红外遥控通信原理">
                <i class="fa fa-chevron-left"></i> 16.2、红外遥控通信原理
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/limh/2014/12/01/mcu109/" rel="prev" title="16.4、温度传感器DS18B20">
                16.4、温度传感器DS18B20 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          

  
    <div class="comments" id="comments">
      <div id="SOHUCS"></div>
    </div>

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      

      <section class="site-overview-wrap sidebar-panel sidebar-panel-active">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <img class="site-author-image" itemprop="image" src="/limh/images/avatar.jpg" alt="土逗牛肉">
            
              <p class="site-author-name" itemprop="name">土逗牛肉</p>
              <p class="site-description motion-element" itemprop="description"></p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/limh/archives/">
              
                  <span class="site-state-item-count">255</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/limh/categories/index.html">
                  <span class="site-state-item-count">8</span>
                  <span class="site-state-item-name">分类</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-tags">
                <a href="/limh/tags/index.html">
                  <span class="site-state-item-count">7</span>
                  <span class="site-state-item-name">标签</span>
                </a>
              </div>
            

          </nav>

          

          
            <div class="links-of-author motion-element">
                
                  <span class="links-of-author-item">
                    <a href="https://github.com/limhGeek" target="_blank" title="GitHub">
                      
                        <i class="fa fa-fw fa-github"></i>GitHub</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="mailto:1591054297@qq.com" target="_blank" title="E-Mail">
                      
                        <i class="fa fa-fw fa-envelope"></i>E-Mail</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="https://www.jianshu.com/u/f73b243e7cc9" target="_blank" title="简书">
                      
                        <i class="fa fa-fw fa-user-circle-o"></i>简书</a>
                  </span>
                
            </div>
          

          
          

          
          

          

        </div>
      </section>

      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2019</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">土逗牛肉</span>

  
</div>


  <div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Gemini</a> v5.1.4</div>




        







        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
	var str = GetUrlParam('height');
    if (str === '0'){
		document.getElementById('header').style.display = 'none';
		document.getElementById('comments').style.display = 'none';
		document.getElementById('footer').style.display = 'none';
		document.getElementsByClassName('post-footer').item(0).style.display = 'none'
	}
	
    function GetUrlParam(paraName) {
        var url = document.location.toString();
        var arrObj = url.split("?");

        if (arrObj.length > 1) {
            var arrPara = arrObj[1].split("&");
            var arr;

            for (var i = 0; i < arrPara.length; i++) {
                 arr = arrPara[i].split("=");

                if (arr != null && arr[0] === paraName) {
                    return arr[1];
                }
            }
            return "";
        } else {
            return "";
        }
    }
</script>









  












  
  
    <script type="text/javascript" src="/limh/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/limh/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/limh/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/limh/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/limh/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/limh/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/limh/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/limh/js/src/motion.js?v=5.1.4"></script>



  
  


  <script type="text/javascript" src="/limh/js/src/affix.js?v=5.1.4"></script>

  <script type="text/javascript" src="/limh/js/src/schemes/pisces.js?v=5.1.4"></script>



  
  <script type="text/javascript" src="/limh/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/limh/js/src/post-details.js?v=5.1.4"></script>



  


  <script type="text/javascript" src="/limh/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  












  





  

  

  

  
  

  

  

  

</body>
</html>
